﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	//int a = 0xf;
	//printf("%x\n", a); //打印出十六进制
	//printf("%o\n", a); //打印出八进制
	//printf("%d\n", a); //打印出十进制

	//2进制的 01101011，换成8进制：0153，0开头的数字，会被当做8进制。
	//2进制的 01101011，换成16进制：0x6b，16进制表示的时候前面加0x

//移位操作符
	//<< 左移操作符
	//>> 右移操作符
	//注：移位操作符的操作数只能是整数。

//左移操作符
	//移位规则：左边抛弃、右边补0
	//int a = 10;
	////00000000000000000000000000001010 --> 10的补码
	////00000000000000000000000000010100 --> 表示a << 1
	//int b = a << 1; //a向左移动1位
	//printf("a = %d\n", a); //10
	//printf("b = %d\n", b); //20   //a << 1这个表达式的结果为20.但 a 还是原来的值

//右移操作符
	//移位规则：⾸先右移运算分两种：
	//1.逻辑右移：左边用0填充，右边丢弃
	//2.算术右移：左边用原该值的符号位填充，右边丢弃
	//int a = -10;
	////11111111111111111111111111110110 --> -10的补码
	////11111111111111111111111111111011 --> 表示 a >> 1 的补码
	////10000000000000000000000000000101 --> 表示 a >> 1
	//int b = a >> 1; //a向右移动1位
	//printf("a = %d\n", a); //-10
	//printf("b = %d\n", b); //-5

//位操作符：& 、 | 、 ^ 、~
//	& //按位与 
//	| //按位或 
//	^ //按位异或 
//	~ //按位取反 
	//注：他们的操作数必须是整数。

//内存中存储的是补码，打印出来的是原码
// & 按位与
    //对应的二进制位上，两个同时为1，才为1，只要有0就是0
	//int a = -8;
	//int b = 6;
	//int c = a & b;
	////11111111111111111111111111111000 --> -8的补码
	////00000000000000000000000000000110 --> 6的补码
	////00000000000000000000000000000000 --> a & b 的补码 --> 原码
	//printf("c = %d", c);

// | 按位或：
    //对应的二进制位进行或运算，只要有1就是1，两个同时为0才为0
	//int a = -8;
	//int b = 6;
	//int c = a | b;
	////11111111111111111111111111111000 --> -8的补码
	////00000000000000000000000000000110 --> 6的补码
	////11111111111111111111111111111110 --> a | b 的补码
	////10000000000000000000000000000010 --> a | b 的原码 --> -2
	//printf("c = %d\n", c);

// ^ 按位异或：
    //对应的二进制位进行异或操作
    //相同为0，相异为1
	//int a = -8;
	//int b = 6;
	//int c = a ^ b;
	////11111111111111111111111111111000 --> -8的补码
	////00000000000000000000000000000110 --> 6的补码
	////11111111111111111111111111111110 --> a ^ b 的补码
	////10000000000000000000000000000010 --> a ^ b 的原码 --> -2
	//printf("c = %d\n", c);

// ~ 按位取反：
	//int a = -1;
	////11111111111111111111111111111111 --> -1的补码
	////00000000000000000000000000000000 --> ~a 的补码 --> 原码
	//int b = ~a; //按位取反时，符号位也要取反
	//printf("b = %d\n", b); //0

	//int a = 0;
	////00000000000000000000000000000000 --> 0的补码
	////11111111111111111111111111111111 --> ~a 的补码
	////10000000000000000000000000000001 --> ~a 的原码
	//int b = ~a;
	//printf("b = %d\n", b); //-1

//例题：不能创建临时变量（第三个变量），实现两个整数的交换。
	//int a = 3;
	//int b = 5;
	////交换
	//printf("交换前:a = %d b = %d\n", a, b);

	//a = a ^ b; //a' = a ^ b
	//b = a ^ b; //b = a' ^ b = a ^ b ^ b == a
	//a = a ^ b; //a = a' ^ b = a ^ b ^ a == b

	//printf("交换后:a = %d b = %d\n", a, b);
//异或操作符的特点：
	//1.a ^ a = 0 相同的两个数字异或为0
	//2.0 ^ a = a
	//3.a ^ a ^ b == a ^ b ^ a 异或满足交换律

//编写代码实现：求一个整数存储在“内存”中的⼆进制中1的个数
//方法一：
	//int n = 0;
	//int count = 0;
	//scanf("%d", &n);
	////统计n的二进制的补码中1的个数
	//while (n)
	//{
	//	if ((n % 2) == 1)
	//		count++; 
	//	n /= 2;
	//}
	//printf("count = %d\n", count);
  //此方法类似于十进制整数打印每一位
  //此方法对于正数来说可以实现，但对于负数来说会出现问题
  //但其实 int n 改成 unsigned int n 即无符号整形时，此时赋值负数也能解决问题
  //原因是：由于-1在内存中以补码形式存储，-1的补码为32个1，而如果是signed int类型则最高位为符号位，则-1打印时以原码形式打印即转换成十进制为-1
  //而如果是unsigned int类型则最高位为代表数值，即-1的补码反码和原码一样(相当于就是正数)，
  //则 unsigned int -1 以原码形式打印时数值为无符号整数的最大值 2的32次方 - 1

//方法二：
    //int n = 0;
	//int count = 0;
	//scanf("%d", &n);
	////统计n的二进制的补码中1的个数
	//int i = 0;
	//for (i = 0; i < 32; i++)
	//{
	//	if (((n >> i) & 1) == 1)
	//	{
	//		count++;
	//	}
	//}
	//printf("count = %d\n", count);
	////000000001010101000101001010101 --> n
    ////000000000000000000000000000001 --> 1 
    ////000000000000000000000000000001 --> n & 1
    ////n & 1 == 1,说明n的二进制中最低位是1
    ////n & 1 == 0,说明n的二进制中最低位是0

//方法三：
	//int n = 0;
	//int count = 0;
	//scanf("%d", &n);
	////统计n的二进制的补码中1的个数
	//int n = 0;
	//while (n)
	//{
	//	count++;
	//	n = n & (n - 1); // n = n & (n - 1) 这个表达式会让n的二进制中最右边的1消失
	//}
	//printf("count = %d\n", count);

//判断一个数是否是2的次方数
	//int n = 0;
	//int count = 0;
	//scanf("%d", &n);
	//int i = 0;
	//for (i = 0; i < 32; i++)
	//{
	//	if (((n >> i) & 1) == 1)
	//	{
	//		count++;
	//	}
	//}
	//if (count == 1)
	//{
	//	printf("%d是2的次方数", n);
	//}
	//else printf("%d不是2的次方数", n);
    //上述方法三的思路：
	//int n = 0;
	//scanf("%d", &n);
	//if ((n  & (n - 1)) == 0) 
	//{
	//	printf("%d是2的次方数", n);
	//}
	//else printf("%d不是2的次方数", n);

//二进制位置0或者置1
//编写代码将13二进制序列的第5位修改为1，然后再改回0
    //int n = 13;
	//int m = n | (1 << 4); //1 << 4 --> 16
	//printf("%d\n", m); //29

	//n &= m;
	//printf("%d\n", n);//13
	////以下为巧妙的想法：13二进制序列的第5位修改为1：00000000000000000000000000011101 按位与
	////                                              11111111111111111111111111101111(即 1 << 4 的按位取反) 则得到原来的二进制序列
	//n &= (~(1 << 4));
	//printf("%d\n", n);//13

//逗号表达式
    //形式：exp1, exp2, exp3, …expN
    //逗号表达式，从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
	//一个伪代码：
    //a = get_val();
    //count_val(a);
    //while (a > 0)
	//{
	// //业务处理 
	// //...
	// a = get_val();
	// count_val(a);
	//}
	//	return 0;
	//}
    //在这个代码中 a = get_val(); 和 count_val(a); 重复出现了两次，使得代码较冗余
    //二逗号表达式可以使这样类似的代码变得更简洁：
    //while(a = get_val(), count_val(a), a > 0)